package de.bht.fb6.cg1.exercise3.implement;

import java.lang.reflect.Array;

import de.bht.fb6.cg1.exercise3.ColumnVector;
import de.bht.fb6.cg1.exercise3.Ring;

/**
 * This is a column vector implementation.
 */
@SuppressWarnings("serial")
public class ColumnVectorImpl<T extends Number> extends MatrixImpl<T> implements ColumnVector<T> {

	/**
	 * Creates a column vector to a given two-dimensional array.
	 * 
	 * @param vals values of the vector
	 * @param rClass ring type of T
	 */
	public ColumnVectorImpl(final T[][] vals, final Class<? extends Ring<T>> rClass) {
		super(vals, rClass);

		if (getColumns() != 1) {
			throw new IllegalArgumentException("column vector only has 1 column");
		}
	}

	/**
	 * Copys a Column Vector.
	 * 
	 * this is used to get a {@link ColumnVectorImpl} from a matrix.
	 * 
	 * @param <R> Ringtype of the vector
	 * @param vals another vector to figure out the data
	 * @param type Ringtype of the vector
	 */
	public <R extends Ring<T>>ColumnVectorImpl(final ColumnVector<T> vals, final Class<R> type) {
		super(vals, type);

		if (getColumns() != 1) {
			throw new IllegalArgumentException("column vector only has 1 column");
		}
	}

	/**
	 * Create a Column vector from a {@link MatrixImpl}
	 * 
	 * @param v a {@link MatrixImpl} object
	 */
	public ColumnVectorImpl(final MatrixImpl<T> v) {
		super(v);

		if (getColumns() != 1) {
			throw new IllegalArgumentException("column vector only has 1 column");
		}
	}

	/**
	 * Create a new vector from a {@link Ring} array
	 * @param startVal initial values in the ring
	 */
	public ColumnVectorImpl(final Ring<T>[][] startVal) {
		super(startVal);

		if (getColumns() != 1) {
			throw new IllegalArgumentException("column vector only has 1 column");
		}
	}

	/**
	 * Calculates the cross product of the given {@link ColumnVector}s.
	 * 
	 * @param vectors The vectors for the cross product. The amount of vectors must be dimension - 2. Must not be null.
	 * @return The cross product of the vectors.
	 */
	@SuppressWarnings("unchecked")
	@Override
	public ColumnVectorImpl<T> crossProduct(final ColumnVector<T>... vectors) {
		if (vectors.length != getRows()-2) {
			throw new IllegalArgumentException("Not enough vectors to build crossproduct.");
		}

		// values from a new matrix from witch we calculate the determinant.
		// the values are vectors so the determinant is a vector itself
		final ColumnVectorRing<T>[][] values = (ColumnVectorRing<T>[][]) Array.newInstance(ColumnVectorRing.class, getRows(), getRows());

		// create identity vector
		for (int col = 0; col < getRows(); col++) {
			final Ring<T>[][] e_values = newRingArray(getRows(), 1);
			for (int i = 0; i < getRows(); i++) {
				e_values[i][0] = RingZero();
			}
			e_values[col][0] = RingOne();

			values[0][col] = new ColumnVectorRing<T>(new ColumnVectorImpl<T>(e_values));
		}

		// copy the single vector values into a new vector - only from this vector
		for (int col = 0; col < getRows(); col++) {
			final Ring<T>[][] v_values = newRingArray(1,1);
			v_values[0][0] = getRing(col,0);
			values[1][col] = new ColumnVectorRing<T>(new ColumnVectorImpl<T>(v_values));
		}

		// same thing with the other vecotrs
		for (int row = 2; row < getRows(); row++) {
			for (int col = 0; col < getRows(); col++) {
				final Ring<T>[][] v_values = newRingArray(1,1);
				v_values[0][0] = newRingValue(vectors[row - 2].get(col, 0));
				values[row][col] = new ColumnVectorRing<T>(new ColumnVectorImpl<T>(v_values));
			}
		}

		// result is the determinant of the matrix.
		// determinant calculates with the internal Columnvectors so it just need to transposed
		final SquareMatrixImpl<ColumnVectorImpl<T>> A = new SquareMatrixImpl<ColumnVectorImpl<T>>(values);
		return new ColumnVectorImpl<T>(A.getDeterminant(), getRingClass());
	}

	/**
	 * calculate the dot product of both vectors
	 * 
	 * @param vector The second vector.
	 * @return The dot product.
	 */
	@Override
	public T dotProduct(final ColumnVector<T> vector) {
		if (getRows() != vector.getRows()) {
			throw new IllegalArgumentException("Invalid vector size");
		}
		return getTransposed().mult(vector).get(0, 0);
	}
}
